package com.jiawa.train.batch.service;

import com.jiawa.train.batch.req.CronJobReq;
import com.jiawa.train.batch.resp.CronJobResp;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class DynamicSchedulerService {
    private final Scheduler scheduler;

    // 构造器注入
    public DynamicSchedulerService(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    @PostConstruct
    public void startScheduler() throws SchedulerException {
        if (!scheduler.isStarted()) {
            scheduler.start();
            log.info("Quartz Scheduler 已启动");
        }
    }

    // 添加任务
    @Transactional
    public void addJob(CronJobReq request) throws SchedulerException {
        try {
            /*使用直接内嵌的方式来执行
            *  利用了反射进行获取类
            Class<? extends Job> clazz = (Class<? extends Job>) Class.forName(request.getJobClass());
            //创建jobDetail然后和要执行的类进行绑定
            JobDetail jobDetail = JobBuilder.newJob(clazz)
                    .withIdentity(request.getName(), request.getGroup())
                    .usingJobData("description", request.getDescription())
                    .build();
            //创建触发器，触发器的唯一标识和jobDetail的唯一标识不能相同
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(request.getName() + "_CRON", request.getGroup())
                    .withSchedule(CronScheduleBuilder.cronSchedule(request.getCronExpression()))
                    .build();
            *
            * */
            JobDetail jobDetail = buildJobDetail(request); // 复用
            Trigger trigger = buildTrigger(request);    // 复用
            //1、将jobDetail和trigger进行注册到scheduler
            //2、当使用schedulerJob()后，任务和触发器会持久化，quartz会自动实现数据插入数据库的操作
            //3、如果 Cron 表达式满足当前时间（如 0 * * * * ? 每分钟触发），Quartz 会立即触发任务。
            //4、如果 scheduler 未启动（如忘记调用 scheduler.start()），即使 Cron 时间到达，任务也不会执行
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("任务 [{}] 已添加，class={}, group={}", request.getName(), request.getJobClass(), request.getGroup());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("找不到任务类: " + request.getJobClass(), e);
        }
    }


    // 暂停任务
    @Transactional
    public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
    }

    // 恢复任务
    @Transactional
    public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
    }

    // 重新调度任务
    @Transactional
    public void rescheduleJob(CronJobReq request) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(request.getName() + "_CRON", request.getGroup());
        CronTrigger newTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(CronScheduleBuilder.cronSchedule(request.getCronExpression()))
                .build();
        //通过 TriggerKey 定位旧的触发器。
        //创建新的触发器（更新 Cron 表达式或其他配置）。
        //调用 rescheduleJob() 替换旧触发器，无需重新绑定 JobDetail。
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }

    // 删除任务
    @Transactional
    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
    }

    // 查询所有任务
    public List<CronJobResp> listAllJobs() throws SchedulerException {
        List<CronJobResp> jobs = new ArrayList<>();
        for (String group : scheduler.getJobGroupNames()) {
            for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
                jobs.add(buildJobResponse(jobKey));
            }
        }
        return jobs;
    }

    // --- 私有工具方法 ---
    private JobDetail buildJobDetail(CronJobReq request) throws ClassNotFoundException {
        Class<? extends Job> clazz = (Class<? extends Job>) Class.forName(request.getJobClass());
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("description", request.getDescription());
        return JobBuilder.newJob(clazz)
                .withIdentity(request.getName(), request.getGroup())
                .usingJobData(jobDataMap)
                .build();
    }


    private Trigger buildTrigger(CronJobReq request) {
        return TriggerBuilder.newTrigger()
                .withIdentity(request.getName() + "_CRON", request.getGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(request.getCronExpression()))
                .build();
    }


    private CronJobResp buildJobResponse(JobKey jobKey) throws SchedulerException {
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        CronJobResp resp = new CronJobResp();
        resp.setName(jobKey.getName());
        resp.setGroup(jobKey.getGroup());
        resp.setJobClass(jobDetail.getJobClass().getName());
        resp.setDescription(jobDetail.getJobDataMap().getString("description"));

        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        if (!triggers.isEmpty()) {
            CronTrigger trigger = (CronTrigger) triggers.get(0);
            resp.setCronExpression(trigger.getCronExpression());
            resp.setNextFireTime(trigger.getNextFireTime());
            resp.setPreFireTime(trigger.getPreviousFireTime());
            resp.setState(scheduler.getTriggerState(trigger.getKey()).name());
        }
        return resp;
    }


    public void manualTriggerJob(CronJobReq request) {
        JobKey jobKey = JobKey.jobKey(request.getName(), request.getGroup());
        try {
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null) {
                throw new IllegalArgumentException("任务不存在: " + jobKey);
            }
            //当jobDetail存在时则使用schedulerJob(jobKey)直接触发执行
            scheduler.triggerJob(jobKey);
            log.info("手动触发成功: {}, class={}", jobKey, jobDetail.getJobClass().getName());
        } catch (SchedulerException e) {
            throw new RuntimeException("触发失败: " + e.getMessage(), e);
        }
    }



}
